• 问题

    如果方法抛出的异常与它所执行的任务没有明显的联系,这种情形物会使人不知所措。当方法将低层异常在高层继续抛出的时候,往往会发生这种情况。除了使人感到困惑之外,这也让实现细节污染了更高层的API。如果高层的实现在后续的发行版本中发生了变化,它所抛出的异常也可能会跟着发生变化,从而潜在地破坏现有的客户端程序。那么,在高低层API处理异常时应该怎么做呢?

  • 答案

    1. 更高层的实现应该捕获低层的异常,同时抛出可以按照高层抽象进行解释的异常。这种做法被称为异常转译(exceptiontranslation),如下所示:

      try{
          //Use lower-level abstraction to do our bidding
          ...
      }cache(LowerLevelException e){
          throw new HigherLevelException(...);
      }
      

      这种方式要求在高层API中,捕获到底层异常的时候抛出与高层业务意义吻合的高层异常HighLevelException,而不是直接将底层异常继续抛出去。

    2. 一种特殊的异常转译形式称为异常链(exceptionchaining),如果低层的异常对于调试导致髙层异常的问题非常有帮助,使用异常链就很合适。低层的异常(原因)被传到髙层的异常,髙层的异常提供访问方法(Thmwable.getCause)来获得低层的异常。例如:

      try{
          //Use lower-level abstraction to do our bidding
      }cache(LowerLevelException cause){
          throw new HigherLevelException(cause);
      }
      

      HighLevelException的构造器为:

      class HigherLevelException extends Exception {
          HigherLevelExceptionCThrowable cause) {
              super(cause);
          }
      }
      
    3. 面对异常到底应该怎样做?

      • 尽管异常转译与直接在高层将底层的异常继续抛出相比有所改进,但是它也不能被滥用。如有可能,处理来自低层异常的最好做法是,应该极力避免底层异常的发生。有时候,可以在给低层传递参数之前,检査更高层方法的参数的有效性,从而避免低层方法抛出异常;
      • 如果无法避免低层异常,次选方案是,让高层API中通过判断绕开这些异常,从而将高层方法的调用者与低层的问题隔离开来。在这种情况下,可以使用日志工具进行记录。
  • 结论

    如果不能阻止或者处理来自更低层的异常,一般的做法是使用异常转译,除非低层方法碰巧可以保证它抛出的所有异常对髙层也合适才可以将异常从低层传播到高居。异常链对髙层和低层异常都提供了最佳的功能:它允许抛出适当的髙层异常,同时又能捕获底层的原因进行失败分析。

results matching ""

    No results matching ""